package model.editormodel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import view.UtilityConstants;

import model.LogicType;

/**
 * <b>Erbt:</b> <code>LogicEditorElement</code> <br>
 * <b>In Verbindung stehende Klassen:</b> <code>CircuitModel</code><br>
 * <b>Funktion:</b> Repraesentation eines selbsterstellten Bauteils<br>
 * <br>
 * Diese Klasse stellt ein selbsterstelltes Bauteil dar. Zur Speicherung der
 * zugehoerigen Information wird die Klasse <code>CircuitModel</code> verwendet.
 * 
 * @author Team Locsi
 * 
 */
public class LogicSelfdefinedEditorElement extends LogicEditorElement {

	private CircuitModel cModel;
	private Map<String, List<Input>> inputs;

	/**
	 * Dieser Konstruktor erzeugt ein neues
	 * <code>LogicSelfdefinedEditorElement</code>-Objekt.
	 * 
	 * @param distinctName
	 *            <code>String</code> - eindeutiger Name des Bauteils
	 * 
	 * @param x
	 *            <code>double</code> - x-Koordinate des
	 *            <code>LogicSelfdefinedEditorElement</code> in Pixel
	 * @param y
	 *            <code>double</code> - y-Koordinate des
	 *            <code>LogicSelfdefinedEditorElement</code> in Pixel
	 * @param cModel
	 *            <code>CircuitModel</code> - Referenz auf das zugehoerige
	 *            <code>CircuitModel</code>, das das
	 *            <code>LogicSelfdefinedEditorElement</code> repraesentiert.
	 * @throws <code>IllegalArgumetnException</code>, falls null beim
	 *         <code>CircuitModel</code> uebergeben wird
	 */
	public LogicSelfdefinedEditorElement(String distinctName, double x,
			double y, CircuitModel cModel) {

		super(LogicType.SELFDEFINED, distinctName, x, y);

		// Der Uebergabeparameter cModel darf nicht null
		// sein
		if (cModel == null || !cModel.isComponent()) {
			throw new IllegalArgumentException(
					"Übergabeparameter darf nicht 'null' sein bzw. die übergebene Schaltung besitzt nicht die Eigenschaften eines selbstdefinierten Bauteils");
		} else {
			this.cModel = cModel;

			// ausserdem wird im zugehoerigen CircuitModel gespeichert ob es ein
			// Schaltwerk oder Schaltnetz ist
			this.cModel.setLogicCircuitType();
			this.inputs = new HashMap<String, List<Input>>();
			createSDInputsAndOutputs();
		}
	}

	/**
	 * In dieser Methode werden die Inputs von einem selbstdefinierten Bauteil
	 * gesetzt. Es werden dabei keine neuen Inputs/Outputs erzeugt, sondern
	 * einfach die Referenz von den Inputs der darunterliegenden Elemente
	 * genommen.
	 * 
	 * Bei Inputs wird zusaetzlich noch eine Map angelegt die eine Liste von
	 * Inputs speichert. Beispielsweise koennte es sein, dass ein Schalter mit
	 * zwei Inputs verbunden war, dann hat das Bauteil ja nur einen sichtbaren
	 * Input, allerdings muss intern die Verbindung zu beiden darunterliegenden
	 * Inputs hergestellt werden. 
	 */
	private void createSDInputsAndOutputs() {
		int i = 1;

		// es werden alle bauteile des cModels vom selbstdefinierten Bauteil
		// durchgegangen
		for (LogicEditorElement lEE : cModel.getElementsOnEditor().values()) {

			// wenn ein schalter oder eine clock gefunden wird, wird ein Eintrag
			// in der inputs-Map
			// oben erstellt und alle inputs die mit diesem schalter/clock
			// verbunden waren
			// in die liste von inputs hinzugefuegt die dieser eintrag in der
			// map
			// als value hat.
			if (lEE.getType() == LogicType.SWITCH
					|| lEE.getType() == LogicType.CLOCK) {
				List<Input> inputsToRemove = new ArrayList<Input>();
				for (Output out : lEE.getOutputs().values()) {
					for (Input in : out.getTargetInputs()) {
						in.setSourceOutput(null);
						this.getInputs().put(lEE.getName(), in);
						inputsToRemove.add(in);
						in.setMySuperElement(this);
						in.setXRelToElement(3);
						in.setYRelToElement(i * 20);
					}
					i++;
				}

				inputs.put(lEE.getName(), inputsToRemove);

				// wenn eine lampe gefunden wird, wird der output genommen
				// der mit dieser lampe verbunden war und in die normale
				// outputs-map
				// eingetragen
			} else if (lEE.getType() == LogicType.LAMP) {
				for (Input in : lEE.getInputs().values()) {
					if (in.getSourceOutput() != null) {
						in.getSourceOutput().getTargetInputs().remove(in);
						outputs.put(lEE.getName(), in.getSourceOutput());
					}
				}
			}
		}

		i = 1;
		for (Output out : outputs.values()) {
			out.setMySuperElement(this);
			out.setXRelToElement(97 - UtilityConstants.IO_RADIUS * 2);
			out.setYRelToElement(i * 20);

			i++;
		}

		for (LogicEditorElement lEE : cModel.getElementsListOnEditor()) {
			if (lEE.getType() != LogicType.SWITCH
					|| lEE.getType() != LogicType.CLOCK) {
				for (Output out : lEE.getOutputs().values()) {
					out.setMySuperElement(this);
				}

			}
		}
	}

	/**
	 * Gibt die Map die Listen von Inputs speichert zurück.
	 * 
	 * @return Map<String, List<Input>>
	 */
	public Map<String, List<Input>> getListOfInputs() {
		return this.inputs;
	}

	/**
	 * Gibt das zugehoerige <code>CircuitModel</code> des
	 * <code>LogicSelfdefinedEditorElement</code> zurueck.
	 * 
	 * @return <code>CircuitModel</code> - das zugehoerige
	 *         <code>CircuitModel</code>
	 */
	public CircuitModel getcModel() {
		return this.cModel;
	}

	/**
	 * Diese Methode greift auf die gespeicherte Information des
	 * <code>CircuitModel</code> zurueck. Diese gibt Auskunft ob es sich bei
	 * diesem selbstdefinierten Bauteil um ein Schaltnetz oder Schalterk
	 * handelt.
	 * 
	 * @return <code>boolean<code> - <code>true</code> falls Schaltwerk,
	 *         ansonsten <code>false</code>
	 */
	public boolean getLogicCircuitType() {
		return this.cModel.getLogicCircuitType();
	}
}